package com.sendi.common.dao;

import java.io.Serializable;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.Order;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import com.sendi.common.util.Page;
import com.sendi.common.util.QueryRule;
import com.sendi.common.util.QueryRuleUtils;
import com.sendi.common.util.StringUtils;

public class CommonDaoHibernate extends HibernateDaoSupport {
	public boolean exists(Class entityClass, Serializable id)
	{
	    Object entity = getHibernateTemplate().get(entityClass, id);
	    return (entity == null);
	}
	public Object get(Class entityClass, Serializable id){
		return getHibernateTemplate().get(entityClass, id);
	}
	public boolean contains(Object obj) throws DataAccessException {
		return getHibernateTemplate().contains(obj);
	}
	protected List<Order> getOrderFromQueryRule(QueryRule queryRule)
	{
	    List orders = new ArrayList();
	    for (QueryRule.Rule rule : queryRule.getRuleList()) {
	    	switch (rule.getType())
	    	{
	    	case 101:
	    		if (StringUtils.isNotEmpty(rule.getPropertyName()))
	    			orders.add(Order.asc(rule.getPropertyName()));
	    		break;
	    	case 102:
	    		if (StringUtils.isNotEmpty(rule.getPropertyName())) {
	    			orders.add(Order.desc(rule.getPropertyName()));
	    		}
	    	}
	    }
	    return orders;
	}
	public List find(final Class entityClass,final QueryRule queryRule)
	{
	    List list = (List)getHibernateTemplate().execute(new HibernateCallback()
	    {
	    	public Object doInHibernate(Session session) throws SQLException
	    	{
		        Criteria criteria = session.createCriteria(entityClass);
		        QueryRuleUtils.createCriteriaWithQueryRule(criteria,queryRule);
		
		        List<Order> orders = CommonDaoHibernate.this.getOrderFromQueryRule(queryRule);
		        for (Order o : orders) {
		          criteria.addOrder(o);
		        }
		        return criteria.setFirstResult(0).list();
	    	}
	    });
	    return list;
  	}
	public Object getUnique(Class entityClass,QueryRule queryRule){
	    List list = find(entityClass, queryRule);
	    if (list.size() == 0)
	    	return null;
	    if (list.size() == 1) {
	    	return list.get(0);
	    }
	    throw new IllegalStateException("findUnique return " + list.size() + " record(s).");
	}
	public Object getUnique(Class entityClass,String propertyName,Object value){
		QueryRule queryRule = QueryRule.getInstance();
	    queryRule.addEqual(propertyName, value);
	    List list = find(entityClass, queryRule);
	    if (list.size() == 0)
	    	return null;
	    if (list.size() == 1) {
	    	return list.get(0);
	    }
	    throw new IllegalStateException("findUnique return " + list.size() + " record(s).");
	}
	public void save(Object obj){
		getHibernateTemplate().saveOrUpdate(obj);
	}
	public void saveOrUpdate(Object obj) throws DataAccessException {
		this.getHibernateTemplate().saveOrUpdate(obj);
	}
	public void update(Object obj){
		getHibernateTemplate().update(obj);
	}
	public void delete(Object obj){
		getHibernateTemplate().delete(obj);
	}
	protected static String removeSelect(String hql)
	{
	    int beginPos = hql.toLowerCase().indexOf("from");
	    return hql.substring(beginPos);
	}

	protected static String removeOrders(String hql)
	{
	    Pattern p = Pattern.compile("order\\s*by[\\w|\\W|\\s|\\S]*", 2);
	    Matcher m = p.matcher(hql);
	    StringBuffer sb = new StringBuffer();
	    while (m.find()) {
	      m.appendReplacement(sb, "");
	    }
	    m.appendTail(sb);
	    return sb.toString();
	}
	public long getCount(String hql, Object[] values)
	{
	    String countQueryString = " select count (*) " + removeSelect(removeOrders(hql));
	    List countlist = getHibernateTemplate().find(countQueryString, values);
	    long totalCount = ((Long)countlist.get(0)).longValue();
	    return totalCount;
	}
	public Page findByHql(final String hql,final int startIndex,final int pageSize,final Object[] values)
	{
		  String countQueryString = " select count (*) " + removeSelect(removeOrders(hql));
		  List countlist = getHibernateTemplate().find(countQueryString, values);
		  long totalCount = 0L;

		  if (countlist != null) {
			  if (countlist.size() == 1) {
				  if (hql.indexOf("group by") != -1)
					  totalCount = Long.valueOf(countlist.size()).longValue();
				  else
					  totalCount = ((Long)countlist.get(0)).longValue();
			  }
			  else {
				  totalCount = Long.valueOf(countlist.size()).longValue();
			  }
		  }
		  if (totalCount < 1L) {
			  return new Page();
		  }
		  final long total = totalCount;

		  List list = getHibernateTemplate().executeFind(new HibernateCallback()
		  {
		      public Object doInHibernate(Session session) throws SQLException {
		        Query query = session.createQuery(hql);
		        if (values != null) {
		            for (int i = 0; i < values.length; ++i) {
		            	query.setParameter(i, values[i]);
		            }
		        }
		        query.setFirstResult(startIndex);
		        query.setMaxResults(pageSize);
		        return query.list();
		      }
		  });
		  Page page = new Page(startIndex,total, pageSize,list);
          return page;
	}
	public List findByHql(String hql,Object[] values){
		return getHibernateTemplate().find(hql, values);
	}
	
	public List findDatasByHqlWithPage(final String hql, final int start,final int limit){
		List list = getHibernateTemplate().executeFind(new HibernateCallback() {
		    public Object doInHibernate(Session session)
		      throws HibernateException, SQLException {
			     Query query = session.createQuery(hql).setFirstResult(start).setMaxResults(limit);
			     return query.list();
		    }
		});
		return list;
	}
}
